package com.poizon.infrastructure.idem.core.recovery;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.poizon.infrastructure.common.util.RetryUtil;
import com.poizon.infrastructure.idem.core.domain.entity.IdemEntity;
import com.poizon.infrastructure.idem.core.domain.value.config.IdemConstants;
import com.poizon.infrastructure.idem.core.domain.value.enums.IdemDbStoreType;
import com.poizon.infrastructure.idem.core.repo.IIdemDbStoreRepo;
import lombok.extern.slf4j.Slf4j;

import javax.validation.constraints.NotBlank;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static com.poizon.infrastructure.idem.core.domain.value.config.IdemConstants.DB_ERROR_THRESHOLD;
import static com.poizon.infrastructure.idem.core.domain.value.config.IdemConstants.dbErrorCounter;

/**
 * 故障转移/幂等框架底层存储
 */
@Slf4j
public class IdemDbStoreFailover {
    // todo: 找个地方将故障转移期间出错的一些幂等数据全部收集起来,方便后续回复
    private static final ReentrantLock LOCK = new ReentrantLock();

    /**
     * 故障自动转移(单线程)
     */
//    public static Boolean switchToAnotherDbStore() {
//        if (LOCK.tryLock()) {
//            // 当前存储类型
//            String currentDbType = IdemConstants.IDEM_DB_STORE_TYPE;
//
//            log.warn(">幂等框架当前存储类型:{}", currentDbType);
//
//            // 错误未到达阈值
//            if (dbErrorCounter.getAndIncrement() <= DB_ERROR_THRESHOLD) {
//                log.warn(">当前幂等响应更新,错误未达到阈值,不进行故障转移,请人工观察是否是网络问题");
//                return false;
//            }
//
//            // 下面这段,只需要有一个线程去完成即可,禁止多线程同时故障转移
//            log.warn(">当前幂等响应更新,错误到达阈值,自动故障转移,请根据日志手动处理异常响应刷新");
//
//            // 错误达到阈值,自动故障转移
//            IdemDbStoreType newDbStoreType = IdemDbStoreType.getDbStoreType(IdemConstants.IDEM_DB_STORE_TYPE);
//
//            while ((newDbStoreType = findNormalIdemDbStoreType(newDbStoreType.getDbStoreType())) != null) {
//                log.error(">幂等底层存储,开始自动故障切换:{} -> {}", currentDbType, newDbStoreType.getDbStoreType());
//
//                if (IdemDbStoreFailover.changeIdemDbStoreInstantly(newDbStoreType.getDbStoreType())) {
//                    log.error(">幂等底层存储,自动故障切换:{} -> {} 切换成功", currentDbType, newDbStoreType.getDbStoreType());
//                    break;
//                }
//                log.error(">幂等底层存储,自动故障切换:{} -> {} 切换失败,开始尝试下一次切换", currentDbType, newDbStoreType.getDbStoreType());
//            }
//
//            // 故障转移存储到达上限,无法故障转移
//            if (newDbStoreType == null) {
//                log.error(">幂等底层存储,自动故障切换:{} -> {} 切换失败,请人工处理", currentDbType, newDbStoreType.getDbStoreType());
//                return false;
//            }
//
//            // 重置错误计数器
//            dbErrorCounter.set(0);
//
//            log.warn(">幂等底层存储由{}->{}故障转移成功", currentDbType, newDbStoreType);
//        }
//        return true;
//    }

    /**
     * 查询正常的幂等数据存储类型
     */
    public static IdemDbStoreType findNormalIdemDbStoreType(@NotBlank String dbStoreType) {
        Assert.notBlank(dbStoreType);
        IdemDbStoreType dbStoreTypeEnum = IdemDbStoreType.getDbStoreType(dbStoreType);
        if (dbStoreTypeEnum == null) {
            return null;
        }

        // 获取所有枚举值
        List<IdemDbStoreType> allTypes = Arrays.stream(IdemDbStoreType.values()).sorted(Comparator.comparingInt(IdemDbStoreType::getPriority)).filter(s -> {
                    return dbStoreTypeEnum.getPriority() < s.getPriority();
                })
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(allTypes)) {
            return allTypes.get(0);
        }

        return null;
    }

    /**
     * 立刻变更幂等底层的存储
     */
    public static Boolean changeIdemDbStoreInstantly(@NotBlank String newDbType) {
        Assert.notBlank(newDbType);

        if (!IdemDbStoreType.isExist(newDbType)) {
            return false;
        }
        IIdemDbStoreRepo idemDbStoreRepo;
        try {
            idemDbStoreRepo = SpringUtil.getBean(newDbType, IIdemDbStoreRepo.class);
        } catch (Exception e) {
            log.error(StrUtil.format("[通用幂等框架] 未找到幂等存储类型={}的实现类", newDbType), e);
            return false;
        }

        if (idemDbStoreRepo == null) {
            log.error("[通用幂等框架] 变更幂等存储实现={},当前存储未找到实现类", newDbType);
            return false;
        }

        log.info("[通用幂等框架] 变更幂等存储标识={}", idemDbStoreRepo);
        IdemEntity.iIdemDbStoreRepo = idemDbStoreRepo;
        log.info("[通用幂等框架] 变更幂等存储实现={}", newDbType);
        IdemConstants.IDEM_DB_STORE_TYPE = newDbType;
        log.info("[通用幂等框架] 幂等存储={}切换成功", newDbType);

        return true;
    }
}
